import os
import socket
import network
import websocket_helper
from time import sleep
from ws_connection import WebSocketConnection, ClientClosedError
# Dict for registed handlers of all paths
handlers = {}
# Function of handler for request not found
notFoundHandler = None
# The path to the web documents on MicroPython filesystem
docPath = "/"
maxContentLength = 1024

class WebSocketClient:
    def __init__(self, conn):
        self.connection = conn

    def process(self):
        pass


class WebSocketServer:
    def __init__(self, page, max_connections=1):
        self._listen_s = None
        self._clients = []
        self._max_connections = max_connections
        self._page = page
        self.ap_str=""

    def _setup_conn(self, port, accept_handler):
        self._listen_s = socket.socket()
        self._listen_s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        ai = socket.getaddrinfo("0.0.0.0", port)
        addr = ai[0][4]

        self._listen_s.bind(addr)
        self._listen_s.listen(1)
        if accept_handler:
            self._listen_s.setsockopt(socket.SOL_SOCKET, 20, accept_handler)
        for i in (network.AP_IF, network.STA_IF):
            iface = network.WLAN(i)
            if iface.active():
                print("WebSocket started on ws://%s:%d" % (iface.ifconfig()[0], port))
                self.ap_str = str(iface.ifconfig()[0])+"_"+str(port)

    def handle(self, sk):
        """Processing new GET request
        """
        global docPath, handlers
        try: # capture timeout for wainting a line
            currLine = str(sk.readline(), 'utf-8')
        except:
            currLine = "" # readline timeout (not a complete line) 
        request = currLine.split(" ")
        if len(request) != 3: # Discarded if it's a bad header
            return
        (method, url, version) = request
        if "?" in url: # Check if there's query string?
            (path, query) = url.split("?", 2)
        else:
            (path, query) = (url, "")
        args = {}
        contentType = ""
        content = b""
        contentLength = 0
        
        if query: # Parsing the querying string
            argPairs = query.split("&")
            for argPair in argPairs:
                arg = argPair.split("=")
                args[arg[0]] = arg[1]
                
        while True: # Read until blank line after header
            header = sk.readline()
            if header.startswith(b"Content-Length"):
                (key, contentLengthStr) = str(header).split(" ")
                contentLength = int(contentLengthStr[0:-5])
                if (contentLength > maxContentLength):
                    # err(sk, "400", "Bad Request")
                    return
            if (header.startswith(b"Content-Type")):
                (key, contentType) = str(header).split(" ")
                contentType = contentType[0:-5]
            if (header == b""):
                return
            if (header == b"\r\n" and contentLength > 0):
                while(len(content) < contentLength):
                    content = content + sk.recv(contentLength)
                    if (len(content) > maxContentLength):
                        # err(socket, "400", "Bad Request")
                        return
                break
            elif header == b"\r\n":
                break
        
        # Check for supported HTTP version
        if version != "HTTP/1.0\r\n" and version != "HTTP/1.1\r\n":
            self.err(sk, "505", "Version Not Supported")
        elif (method != "GET" and method != "PUT" and method != "POST"):  # Only accept GET,PUT and POST request
            self.err(sk, "501", "Not Implemented")
        elif path in handlers: # Check for registered path
            # print("contentType:"+contentType)
            # print("content:"+str(content))
            # print("method:"+method)
            # print("path:"+path)
            # print("args:"+str(args))
            # print("get handlers:"+str(handlers))
            handlers[path](sk, args)
        elif not path.startswith(docPath): # Check for wrong path
            self.err(sk, "400", "Bad Request")
        else: # find file in the document path
            # filePath = path
            # print("Serve File " + filePath)
            self._serve_page(sk)
    def set_handler_path(self, path, handler):
        """Register handler for processing request of specified path
        """
        global handlers
        handlers[path] = handler
        print("set handlers:"+str(handlers))
        
    def err(self, sk, code, message):
        """Respong error meesage to client
        """
        sk.write("HTTP/1.1 " + code + " " + message + "\r\n")
        sk.write("Content-Type: text/html\r\n\r\n")
        sk.write("<h1>" + message + "</h1>")
    def response_body(self, socket, args):
        chunk_size = 256  # 每次截断的长度
        if len(str(args)) > chunk_size:
            my_string = str(args) # 假设这里是长度为 1024 的字符串
            for i in range(0, len(my_string), chunk_size):
                chunk = my_string[i:i+chunk_size]
                # print(chunk)
                socket.write(chunk)
        else:
            socket.write(str(args))
    def response_header(self, socket, code):
        """Response successful message or webpage to client
        """
        content_type = "text/plain"
        socket.write("HTTP/1.1 " + code + " OK\r\n")
        socket.write("Content-Type: " + content_type + "\r\n\r\n")

    def response_ok(self, socket, code, args):
        """Response successful message or webpage to client
        """
        content_type = "text/plain"
        socket.write("HTTP/1.1 " + code + " OK\r\n")
        socket.write("Content-Type: " + content_type + "\r\n\r\n")
        chunk_size = 256  # 每次截断的长度
        if len(str(args)) > chunk_size:
            my_string = str(args) # 假设这里是长度为 1024 的字符串
            for i in range(0, len(my_string), chunk_size):
                chunk = my_string[i:i+chunk_size]
                # print(chunk)
                socket.write(chunk)
        else:
            socket.write(str(args))
    def _accept_conn(self, listen_sock):
        cl, remote_addr = listen_sock.accept()
        print("Client connection from:", remote_addr)

        if len(self._clients) >= self._max_connections:
            # Maximum connections limit reached
            cl.setblocking(True)
            cl.sendall("HTTP/1.1 503 Too many connections\n\n")
            cl.sendall("\n")
            #TODO: Make sure the data is sent before closing
            sleep(0.1)
            cl.close()
            return
        
        try:
            # print("server handshake.111.\n")
            # websocket_helper.server_handshake(cl)
            self.handle(cl);
            cl.close()
        except OSError:
            # Not a websocket connection, serve webpage
            # print("server page return")
            # self._serve_page(cl)
            cl.close()
            return

        self._clients.append(self._make_client(WebSocketConnection(remote_addr, cl, self.remove_connection)))

    def _make_client(self, conn):
        return WebSocketClient(conn)

    def _serve_page(self, sock):
        try:
            # sock.setblocking(0)
            # data = sock.recv(512).decode('utf-8');
            # print("sock read="+str(len(data)))
            # print("->"+data)
            sock.sendall('HTTP/1.1 200 OK\nConnection: close\nServer: WebSocket Server\nContent-Type: text/html\n')
            length = os.stat(self._page)[6]
            sock.sendall('Content-Length: {}\n\n'.format(length))
            # Process page by lines to avoid large strings
            with open(self._page, 'r') as f:
                for line in f:
                    sock.sendall(line)
        except OSError:
            # Error while serving webpage
            pass
        sock.close()

    def stop(self):
        if self._listen_s:
            self._listen_s.close()
        self._listen_s = None
        for client in self._clients:
            client.connection.close()
        print("Stopped WebSocket server.")

    def start(self, port=80):
        if self._listen_s:
            self.stop()
        self._setup_conn(port, self._accept_conn)
        print("Started WebSocket server.")

    def process_all(self):
        for client in self._clients:
            client.process()

    def remove_connection(self, conn):
        for client in self._clients:
            if client.connection is conn:
                self._clients.remove(client)
                return
